#ifndef _CUDA_DEVICE_ARRAY_HPP_
#define _CUDA_DEVICE_ARRAY_HPP_

#include <cuda.h>
#include <cuda_runtime.h>

namespace cuda {
    namespace device {

	template<typename T>
	struct device_ptr;

	template<>
	struct device_ptr<void> {
	    void free();
	    device_ptr(void* data = NULL, size_t size = 0);
	    device_ptr(size_t size);
	    operator void*() { return data_; }
	    operator const void*() const { return data_; }
	protected:
	    void *data_;
	    size_t size_;
	};

	template<typename T>
	struct device_ptr : device_ptr<void> {
	    typedef device_ptr<void> base;
	    device_ptr(T *data = NULL, size_t size = 0) : base(data, size) {}
	    device_ptr(size_t size) : base(size*sizeof(T)) {}
	    operator T*() { return (T*)base::data_; }
	    operator const T*() const { return (const T*)base::data_; }
	};


	template< typename T, size_t N>
	struct device_array_detail {
	    typedef T* reference;
	    typedef const T* const_reference;
	    __device__
	    static reference get(T* data, int i) { return data + i*N; }
	    __device__
	    static const_reference get(const T* data, int i) { return data + i*N; }
	};

	template< typename T>
	struct device_array_detail<T,1> {
	    typedef T& reference;
	    typedef const T& const_reference;
	    __device__
	    static reference get(T* data, int i) { return data[i]; }
	    __device__
	    static const_reference get(const T* data, int i) { return data[i]; }
	};



	template< typename T, size_t N = 1>
	struct const_array {
	    typedef device_array_detail<T,N> detail;
	    typedef typename detail::reference reference;
	    typedef typename detail::const_reference const_reference;
	    const_array(const device_ptr<T> &data = device_ptr<T>(),
			size_t size = 0)
		: data_(data), size_(size) {}
	    __host__ __device__ size_t size() const { return size_; }
	    __host__ __device__ bool empty() const { return size_ == 0; }
	    __host__ __device__ const device_ptr<T> data() const { return  data_;}
	    __host__ __device__ operator const T*() const { return data_; }
	    __device__ const_reference operator[](int i) const {
		return detail::get(data_, i);
	    }
	private:
 	    device_ptr<T> data_;
	    size_t size_;
	};

	// template<size_t N >
	// struct device_array< void, N > {
	//     //device_array(device_ptr<T> &data) : data_(data) {}
	//     device_array(size_t size = 0) : data_(N*size) {}
	//     __host__ __device__ size_t size() const { return size_; }
	//     __host__ __device__ bool empty() const { return size_ == 0; }
	//     __host__ __device__ operator  void*() { return data_; }
	//     __host__ __device__ operator const void*() const { return data_; }
	// private:
	//     size_t size_;
	//     device_ptr<void> data_;
	// };

    }
}

#endif /* _CUDA_DEVICE_ARRAY_HPP_ */
